﻿﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using StarTrek.Ship;
using StarTrek.Space;
﻿using StarTrek.Weapon;

namespace StarTrek.World
{
    public class Galaxy
    {
        private static Galaxy Singleton = null;
        private Quadrant[, ,] cGalaxy; 
        private iShip cCurrentShip;
        private List<Pair<iShip,EnemyCaptian>> EvilVessels = new List<Pair<iShip,EnemyCaptian>>();
        

        public static Galaxy Instance
        {
            get
            {
                if (Singleton == null)
                {
                    Singleton = LoadWorld(new System.IO.FileInfo(@"C:\Documents and Settings\jasonm\My Documents\Visual Studio 2008\Projects\StarTrek\StarTrek\WorldDef.xml"));
                }
                return Singleton;
            }
        }

        public iShip CurrentShip
        {
            get
            {
                System.Diagnostics.Debug.Assert(cCurrentShip != null, "Ship is created in load, why no ship?");
                return cCurrentShip;
            }
        }

        public iSpaceItem GetLocation(QuadrantPoint point)
        {
            return GetLocation(point.X, point.Y, point.Z);
        }

        public iSpaceItem GetLocation(int x, int y, int z)
        {
            return cGalaxy[x, y, z].GetOccupyableLocation();
        }

        public iSpaceItem GetLocation(int x, int y, int z, int secX, int secY)
        {
            return cGalaxy[x, y, z].GetSpaceItem(secX, secY);
        }

        public void AddItem(int X, int Y, int Z, Quadrant Item)
        {
            Quadrant old = cGalaxy[X, Y, Z];
            if (old != null)
                old.Parent = null;

            Item.Parent = this;
            cGalaxy[X, Y, Z] = Item;
        }

        GalaxyInfo BuildInfo()
        {
            throw new NotImplementedException();
            return null;
        }

        public float CalculateDistance(iSpaceItem Source, iSpaceItem Target)
        {
            float QUAD_TO_ITEM_CONV = 11; //A quadrant take ~11 units of engery to cross
            QuadrantPoint sPoint;
            QuadrantPoint tPoint;
            float rVal = float.PositiveInfinity;

            if (FindSpaceItem(Source, out sPoint) && FindSpaceItem(Target, out tPoint))
            {
                rVal = Convert.ToSingle(Math.Sqrt(sPoint.X * sPoint.X + sPoint.Y * sPoint.Y + sPoint.Z * sPoint.Z));
                rVal *= QUAD_TO_ITEM_CONV;
            }

            return rVal;
        }

        public iShip GetVessel(string VesselName)
        {
            return EvilVessels.Find(
                (Pair<iShip,EnemyCaptian> itm) => itm.Left.ShipName == VesselName
                ).Left;
        }
        public List<iShip> GetLocalVessels(Quadrant Quad)
        {
            return EvilVessels.ConvertAll(
                    (Pair<iShip, EnemyCaptian> itm) => itm.Left
                ).FindAll(
                    (v)=>v.CurrentLocation.Parent == Quad
                );
        }
        
        private bool FindSpaceItem(iSpaceItem Item, out QuadrantPoint rVal)
        {
            for (int i = 0; i <= cGalaxy.GetUpperBound(0); i++)
            {
                for (int j = 0; j <= cGalaxy.GetUpperBound(1); j++)
                {
                    for (int k = 0; k <= cGalaxy.GetUpperBound(2); k++)
                    {
                        if (cGalaxy[i, j, k].Contains(Item))
                        {
                            rVal = new QuadrantPoint(i, j, k);
                            return true;
                        }
                    }
                }

            }
            rVal = null;
            return false;
        }

        
        public void RemoveShip(iShip ship)
        {
            Pair<iShip, EnemyCaptian> Target = EvilVessels.Find(
                (itm) => itm.Left == ship);
            EvilVessels.Remove(Target);

            //Notify Ui?
        }
 
        public static Galaxy LoadWorld(FileInfo target)
        {
            switch (target.Extension.ToUpper())
            {
                case ".XML":
                    return LoadWorldXML(target);
                default:
                    throw new NotImplementedException();
            }
            return null;
        }

        private static Galaxy LoadWorldXML(FileInfo target)
        {
            Galaxy rVal = new Galaxy();
            rVal.cCurrentShip = new StarFleetCon("Enterprise NCC1701-C");
            XmlDocument doc = new XmlDocument();

            doc.Load(target.OpenText());

            XmlNodeList worlds = doc.GetElementsByTagName("World");

            if (worlds.Count > 1)
                throw new IOException("Invalid Configuration file");

            //Load world Shape
            QuadrantPoint size = QuadrantPoint.ParseVector(worlds[0].Attributes["Shape"].Value);
            rVal.cGalaxy = new Quadrant[size.X, size.Y, size.Z];

            //for each Quadrant item - load quadrant.

            QuadrantPoint QuadLoc = null;
            foreach (XmlNode item in worlds[0].ChildNodes)
            {
                if (item.Name == "Quadrant")
                {
                    QuadLoc = QuadrantPoint.ParseVector(item.Attributes["Loc"].Value);
                    rVal.AddItem(QuadLoc.X, QuadLoc.Y, QuadLoc.Z, Quadrant.FromXML(item));
                    System.Diagnostics.Debug.Write(rVal.cGalaxy[QuadLoc.X, QuadLoc.Y, QuadLoc.Z].ToString());
                }
                if (item.Name == "Ship")
                {
                    const string SHIP_NAMESPACE = "StarTrek.Ship.";
                    iShip ship = System.Reflection.Assembly.GetExecutingAssembly()
                        .GetType(SHIP_NAMESPACE + item.Attributes["Type"].Value)
                        .GetConstructor(new Type[] { typeof(string) })
                        .Invoke(new object[] { item.Attributes["Name"].Value }) as iShip;
                    

                    QuadLoc = QuadrantPoint.ParseVector(item.Attributes["Loc"].Value);
                    QuadrantPoint sec = QuadrantPoint.ParseVector(item.Attributes["Sec"].Value + ",0");
                    ship.CurrentLocation = rVal.GetLocation(QuadLoc.X, QuadLoc.Y, QuadLoc.Z, sec.X, sec.Y);
                    ship.CurrentLocation.PresentShip = ship;

                    //Update Instance with new innerards
                    foreach (XmlNode shipNode in item.ChildNodes)
                    {
                        switch(shipNode.Name.ToUpper())
                        {
                            case "POWERUNITS":
                                float val = float.NegativeInfinity;
                                float.TryParse(shipNode.InnerText, out val);
                                if (val != float.NegativeInfinity)
                                    ship.PowerUnit = val;
                                break;
                            case "WEAPONS":
                                WeaponList list = WeaponList.LoadWeapon(shipNode, ship as BaseShip);
                                break;
                            default:
                                throw new NotImplementedException();
                        }
                    }

                    //Create a AI personality to command said ship.
                    rVal.EvilVessels.Add(new Pair<iShip, EnemyCaptian>(ship,new EnemyCaptian()));
                    System.Diagnostics.Debug.WriteLine(string.Format("***Built vessel, {0} at loc: {1},{2}", ship.ShipName, item.Attributes["Loc"].Value, item.Attributes["Sec"].Value));
                    foreach (string s in (ship as BaseShip).ShipReport())
                    {
                        System.Diagnostics.Debug.WriteLine(s);
                    }
                    System.Diagnostics.Debug.WriteLine(string.Format("***End Built vessel, {0} ", ship.ShipName));
                }
            }
            //Clean up void Quadrants
            //Fill nulls with space locations.

            for (int i = 0; i < size.X; i++)
            {
                for (int j = 0; j < size.Y; j++)
                {
                    for (int k = 0; k < size.Z; k++)
                    {
                        if (rVal.cGalaxy[i, j, k] == null)
                            rVal.AddItem(i, j, k, Quadrant.FromString(new string('0', 64))); //Create a null Quadrant
                    }
                }
            }

            //Place Enterprise in Galaxy
            rVal.CurrentShip.CurrentLocation = rVal.GetLocation(0, 0, 0, 1, 3);
            return rVal;
        }


        public bool AITurn()
        {
            bool updatedPlayer = false;
            EvilVessels.ForEach((itm) => updatedPlayer = updatedPlayer || itm.Right.PerformShipAction(itm.Left));

            return updatedPlayer;
        }
    }

    public class Pair<T, T1>
    {
        private readonly T InnerLeft;
        private readonly T1 InnerRight;

        public T Left  { get{return InnerLeft;}}
        public T1 Right{get{ return InnerRight;}}
        
        public Pair (T lft, T1 Rht)
        {
            InnerLeft = lft;
            InnerRight = Rht;
        }

    }


    public class GalaxyInfo { }
}
